package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.lifted.*;
import gov.nasa.anml.utility.*;

public class Change<S extends SimpleObject<? super S>> extends BinaryExpression<S,S,S> {

	public Change(Expression<S, S> l, Expression<S, ?> r) {
		super(l, r);
	}

	public S value(State s) {
		return right.value(s);
	}


	public PDDL.Time splitFirst(PDDL.Time t) {
		switch(t) {
		case All:
			return PDDL.Time.StartHalf;
		case StartHalf:
			return PDDL.Time.StartHalf;
		case EndHalf:
			return PDDL.Time.EndHalf;
		// if crammed into splitting atomic pieces up....
		case Interim:
			return PDDL.Time.Interim;
		case Start:
			// should be immediately before start: ...start)
			return PDDL.Time.Start;
		case End:
			// should be immediately before end: ...end)
			// instead of (start, end)
			return PDDL.Time.Interim;
		case Timeless:
		default:
			return t;
		}
	}
	
	public PDDL.Time splitRest(PDDL.Time t) {
		switch(t) {
		case All:
			return PDDL.Time.End;
		case StartHalf:
			return PDDL.Time.End;
		case EndHalf:
			return PDDL.Time.End;
		// if forced to split atomic things...
		case Interim:
			return PDDL.Time.End;
		case Start:
			return PDDL.Time.Interim;
		case End:
			return PDDL.Time.End;
		case Timeless:
		default:
			return t;
		}
	}
	
	public void translateStmt(PDDL pddl, Interval unit, PDDL.Time part) {
		ArrayList<PDDL.BooleanExpression> conditions = unit.getPDDLConditions(); 
		ArrayList<PDDL.Effect> effects = unit.getPDDLEffects();
		switch(left.typeCode()) {
		case Boolean:
			assert right.typeCode() == TypeCode.Boolean : "No operation combines booleans and other types directly";
			PDDL.PredicateReference refB = (PDDL.PredicateReference) left.translateLValue(pddl,unit);
			if (refB == null)
				System.err.println("huh?");
			if (right instanceof SimpleBoolean) {
				boolean v = ((SimpleBoolean)right).v;
				switch(part) {
				case Start:
				case End:
					effects.add(pddl.makeEffect(part,refB,v));
					break;
				case Interim:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,!v));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,!v)));
					effects.add(pddl.makeEffect(PDDL.Time.End,refB,v));
					break;
				case All:
				case EndHalf:
				case StartHalf:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,!v));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,!v)));
					effects.add(pddl.makeEffect(PDDL.Time.End,refB,v));
					break;
				default:
					System.err.println("New PDDL.Time constant unaccounted for in Change.");
				}
			} else {
				// Pretend STRIPS assumption works.
				PDDL.BooleanExpression r = (PDDL.BooleanExpression) right.translateExpr(pddl,unit);
				switch(part) {
				case Start:
				case End:
					effects.add(pddl.makeEffect(part,refB,r));
					break;
				case Interim:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,false));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,false)));
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,r));
					break;
				case All:
				case EndHalf:
				case StartHalf:
					effects.add(pddl.makeEffect(PDDL.Time.Start,refB,false));
					conditions.add(pddl.wrap(PDDL.Time.Interim,pddl.makeTest(refB,false)));
					effects.add(pddl.makeEffect(PDDL.Time.End,refB,r));
					break;
				default:
					System.err.println("New PDDL.Time constant unaccounted for in Change.");
				}
			}
			break;
		case Float:
			assert right.typeCode() == TypeCode.Float : "No operation combines floats and non-floats (at present).";
			PDDL.FunctionReference refF = (PDDL.FunctionReference) left.translateLValue(pddl,unit);
			PDDL.FloatExpression v = (PDDL.FloatExpression) right.translateExpr(pddl,unit);
			switch(part) {
			case Start:
			case End:
				effects.add(pddl.makeEffect(part,refF,v));
				break;
			case Interim:
				effects.add(pddl.makeEffect(PDDL.Time.Start,refF,pddl.FloatUndefined));
				// can't realy test for undefined, but, pddl mutual exclusion rules should work...maybe
				effects.add(pddl.makeEffect(PDDL.Time.Start,refF,v));
				break;
			case All:
			case EndHalf:
			case StartHalf:
				effects.add(pddl.makeEffect(PDDL.Time.Start,refF,pddl.FloatUndefined));
				effects.add(pddl.makeEffect(PDDL.Time.End,refF,v));
				break;
			default:
				System.err.println("New PDDL.Time constant unaccounted for in Change.");
			}
			break;
		default:
			System.err.println("Oops!");
		}
	}


}
